{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xLOXFOT5Q40E"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "iiQkM5ZgQ8r2"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uLeF5Nmdef0V"
      },
      "source": [
        "# 量子畳み込みニューラルネットワーク"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "i9Jcnb8bQQyd"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/quantum/tutorials/qcnn\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\"> TensorFlow.orgで表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/quantum/tutorials/qcnn.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\"> Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/quantum/tutorials/qcnn.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示{</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/quantum/tutorials/qcnn.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード/a0}</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4D3xaWBHOIVg"
      },
      "source": [
        "このチュートリアルでは、単純な<a href=\"https://www.nature.com/articles/s41567-019-0648-8\" class=\"external\">量子畳み込みニューラルネットワーク</a>（QCNN）を実装します。QCNN は、*並進的に不変*でもある古典的な畳み込みニューラルネットワークに提案された量子アナログです。\n",
        "\n",
        "この例では、デバイスの量子センサまたは複雑なシミュレーションなど、量子データソースの特定のプロパティを検出する方法を実演します。量子データソースは、励起の有無にかかわらず<a href=\"https://arxiv.org/pdf/quant-ph/0504097.pdf\" class=\"external\">クラスタ状態</a>です。QCNN はこの検出を学習します（論文で使用されたデータセットは SPT フェーズ分類です）。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FnjolLuz8o5C"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Aquwcz-0aHqz"
      },
      "outputs": [],
      "source": [
        "!pip install tensorflow==2.3.1"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "e_ZuLN_N8yhT"
      },
      "source": [
        "TensorFlow Quantum をインストールします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3Pl5PW-ACO9J"
      },
      "outputs": [],
      "source": [
        "!pip install tensorflow-quantum"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TL_LvHXzPNjW"
      },
      "source": [
        "次に、TensorFlow とモジュールの依存関係をインポートします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QytLEAtoejW5"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "import tensorflow_quantum as tfq\n",
        "\n",
        "import cirq\n",
        "import sympy\n",
        "import numpy as np\n",
        "\n",
        "# visualization tools\n",
        "%matplotlib inline\n",
        "import matplotlib.pyplot as plt\n",
        "from cirq.contrib.svg import SVGCircuit"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "j6331ZSsQGY3"
      },
      "source": [
        "## 1. QCNN を構築する"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Qg85u3G--CGq"
      },
      "source": [
        "### 1.1 TensorFlow グラフで回路を組み立てる\n",
        "\n",
        "TensorFlow Quantum（TFQ）には、グラフ内で回路を構築するために設計されたレイヤークラスがあります。たとえば `tfq.layers.AddCircuit` レイヤーがあり、`tf.keras.Layer` を継承しています。このレイヤーは、次の図で示すように、回路の入力バッチの前後いずれかに追加できます。\n",
        "\n",
        "<img src=\"./images/qcnn_1.png\" width=\"700\">\n",
        "\n",
        "次のスニペットには、このレイヤーが使用されています。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FhNf0G_OPLqZ"
      },
      "outputs": [],
      "source": [
        "qubit = cirq.GridQubit(0, 0)\n",
        "\n",
        "# Define some circuits.\n",
        "circuit1 = cirq.Circuit(cirq.X(qubit))\n",
        "circuit2 = cirq.Circuit(cirq.H(qubit))\n",
        "\n",
        "# Convert to a tensor.\n",
        "input_circuit_tensor = tfq.convert_to_tensor([circuit1, circuit2])\n",
        "\n",
        "# Define a circuit that we want to append\n",
        "y_circuit = cirq.Circuit(cirq.Y(qubit))\n",
        "\n",
        "# Instantiate our layer\n",
        "y_appender = tfq.layers.AddCircuit()\n",
        "\n",
        "# Run our circuit tensor through the layer and save the output.\n",
        "output_circuit_tensor = y_appender(input_circuit_tensor, append=y_circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ShZbRZCXkvk5"
      },
      "source": [
        "入力テンソルを調べます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ImRynsUN4BSG"
      },
      "outputs": [],
      "source": [
        "print(tfq.from_tensor(input_circuit_tensor))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xkGU4ZTUk4gf"
      },
      "source": [
        "次に、出力テンソルを調べます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tfff6dJp39Fg"
      },
      "outputs": [],
      "source": [
        "print(tfq.from_tensor(output_circuit_tensor))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "23JeZ7Ns5qy5"
      },
      "source": [
        "以下の例は `tfq.layers.AddCircuit` を使用せずに実行できますが、TensorFlow 計算グラフに複雑な機能を埋め込む方法を理解する上で役立ちます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GcVplt9455Hi"
      },
      "source": [
        "### 1.2 問題の概要\n",
        "\n",
        "*クラスター状態*を準備し、「励起」があるかどうかを検出する量子分類器をトレーニングします。クラスター状態は極めてこじれていますが、古典的コンピュータにおいては必ずしも困難ではありません。わかりやすく言えば、これは論文で使用されているデータセットよりも単純です。\n",
        "\n",
        "この分類タスクでは、次の理由により、ディープ <a href=\"https://arxiv.org/pdf/quant-ph/0610099.pdf\" class=\"external\">MERA</a> のような QCNN アーキテクチャを実装します。\n",
        "\n",
        "1. QCNN と同様に、リングのクラスター状態は並進的に不変である\n",
        "2. クラスター状態は非常にもつれている\n",
        "\n",
        "このアーキテクチャはエンタングルメントを軽減し、単一のキュービットを読み出すことで分類を取得する上で効果があります。\n",
        "\n",
        "<img src=\"./images/qcnn_2.png\" width=\"1000\">\n",
        "\n",
        "「励起」のあるクラスター状態は、`cirq.rx` ゲートがすべてのキュービットに適用されたクラスター状態として定義されます。Qconv と QPool については、このチュートリアルの後の方で説明しています。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jpqtsGJH_I1d"
      },
      "source": [
        "### 1.3 TensorFlow のビルディングブロック\n",
        "\n",
        "<img src=\"./images/qcnn_3.png\" width=\"1000\">\n",
        "\n",
        "TensorFlow Quantum を使ってこの問題を解決する方法として、次を実装することが挙げられます。\n",
        "\n",
        "1. モデルへの入力は回路で、空の回路か励起を示す特定のキュー人における X ゲートです。\n",
        "2. モデルの残りの量子コンポーネントは、`tfq.layers.AddCircuit` レイヤーで作成されます。\n",
        "3. たとえば `tfq.layers.PQC` レイヤーが使用されているとした場合、$\\langle \\hat{Z} \\rangle$ を読み取って、励起のある状態には 1 のラベルと、励起のない状態には -1 のラベルと比較します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oa7Q3m_ThDgO"
      },
      "source": [
        "### 1.4 データ\n",
        "\n",
        "モデルを構築する前に、データを生成することができます。この場合には、クラスター状態に励起がは一斉思案す（元の論文では、より複雑なデータセットが使用されています）。励起は、`cirq.rx` ゲートで表されます。十分に大きい回転は励起と見なされ、`1` とラベル付けされ、十分に大きくない回転は `-1` とラベル付けされ、励起ではないと見なされます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "iUrvTCU1hDgP"
      },
      "outputs": [],
      "source": [
        "def generate_data(qubits):\n",
        "    \"\"\"Generate training and testing data.\"\"\"\n",
        "    n_rounds = 20  # Produces n_rounds * n_qubits datapoints.\n",
        "    excitations = []\n",
        "    labels = []\n",
        "    for n in range(n_rounds):\n",
        "        for bit in qubits:\n",
        "            rng = np.random.uniform(-np.pi, np.pi)\n",
        "            excitations.append(cirq.Circuit(cirq.rx(rng)(bit)))\n",
        "            labels.append(1 if (-np.pi / 2) <= rng <= (np.pi / 2) else -1)\n",
        "\n",
        "    split_ind = int(len(excitations) * 0.7)\n",
        "    train_excitations = excitations[:split_ind]\n",
        "    test_excitations = excitations[split_ind:]\n",
        "\n",
        "    train_labels = labels[:split_ind]\n",
        "    test_labels = labels[split_ind:]\n",
        "\n",
        "    return tfq.convert_to_tensor(train_excitations), np.array(train_labels), \\\n",
        "        tfq.convert_to_tensor(test_excitations), np.array(test_labels)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wGsDkZnrhDgS"
      },
      "source": [
        "通常の機械学習と同じように、モデルのベンチマークに使用するトレーニングとテストのセットを作成していることがわかります。次のようにすると、データポイントを素早く確認できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "eLJ-JHOihDgT"
      },
      "outputs": [],
      "source": [
        "sample_points, sample_labels, _, __ = generate_data(cirq.GridQubit.rect(1, 4))\n",
        "print('Input:', tfq.from_tensor(sample_points)[0], 'Output:', sample_labels[0])\n",
        "print('Input:', tfq.from_tensor(sample_points)[1], 'Output:', sample_labels[1])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sFiRlDt_0-DL"
      },
      "source": [
        "### 1.5 レイヤーを定義する\n",
        "\n",
        "上記の図で示すレイヤーを TensorFlow で定義しましょう。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "s2B9geIqLWHK"
      },
      "source": [
        "#### 1.5.1 クラスター状態\n",
        "\n",
        "まず始めに、<a href=\"https://arxiv.org/pdf/quant-ph/0504097.pdf\" class=\"external\">クラスター状態</a>を定義しますが、これには Google が量子回路のプログラミング用に提供している <a href=\"https://github.com/quantumlib/Cirq\" class=\"external\">Cirq</a> フレームワークを使用します。モデルの静的な部分であるため、`tfq.layers.AddCircuit` 機能を使用して埋め込みます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qpQwVWKazU8g"
      },
      "outputs": [],
      "source": [
        "def cluster_state_circuit(bits):\n",
        "    \"\"\"Return a cluster state on the qubits in `bits`.\"\"\"\n",
        "    circuit = cirq.Circuit()\n",
        "    circuit.append(cirq.H.on_each(bits))\n",
        "    for this_bit, next_bit in zip(bits, bits[1:] + [bits[0]]):\n",
        "        circuit.append(cirq.CZ(this_bit, next_bit))\n",
        "    return circuit"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "e9qX1uN740vJ"
      },
      "source": [
        "矩形の <a href=\"https://cirq.readthedocs.io/en/stable/generated/cirq.GridQubit.html\" class=\"external\"><code>cirq.GridQubit</code></a> のクラスター状態回路を表示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9tZt0aAO4r4F"
      },
      "outputs": [],
      "source": [
        "SVGCircuit(cluster_state_circuit(cirq.GridQubit.rect(1, 4)))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4xElWnRf1ZC7"
      },
      "source": [
        "#### 1.5.2 QCNN レイヤー\n",
        "\n",
        "<a href=\"https://arxiv.org/abs/1810.03787\" class=\"external\">Cong and Lukin の QCNN に関する論文</a>を使用して、モデルを構成するレイヤーを定義します。これには次の前提条件があります。\n",
        "\n",
        "- <a href=\"https://arxiv.org/abs/quant-ph/0507171\" class=\"external\">Tucci の論文</a>にある 1 キュービットと 2 キュービットのパラメータ化されたユニタリ―行列\n",
        "- 一般的なパラメータ化された 2 キュービットプーリング演算"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "oNRGOqky2exY"
      },
      "outputs": [],
      "source": [
        "def one_qubit_unitary(bit, symbols):\n",
        "    \"\"\"Make a Cirq circuit enacting a rotation of the bloch sphere about the X,\n",
        "    Y and Z axis, that depends on the values in `symbols`.\n",
        "    \"\"\"\n",
        "    return cirq.Circuit(\n",
        "        cirq.X(bit)**symbols[0],\n",
        "        cirq.Y(bit)**symbols[1],\n",
        "        cirq.Z(bit)**symbols[2])\n",
        "\n",
        "\n",
        "def two_qubit_unitary(bits, symbols):\n",
        "    \"\"\"Make a Cirq circuit that creates an arbitrary two qubit unitary.\"\"\"\n",
        "    circuit = cirq.Circuit()\n",
        "    circuit += one_qubit_unitary(bits[0], symbols[0:3])\n",
        "    circuit += one_qubit_unitary(bits[1], symbols[3:6])\n",
        "    circuit += [cirq.ZZ(*bits)**symbols[6]]\n",
        "    circuit += [cirq.YY(*bits)**symbols[7]]\n",
        "    circuit += [cirq.XX(*bits)**symbols[8]]\n",
        "    circuit += one_qubit_unitary(bits[0], symbols[9:12])\n",
        "    circuit += one_qubit_unitary(bits[1], symbols[12:])\n",
        "    return circuit\n",
        "\n",
        "\n",
        "def two_qubit_pool(source_qubit, sink_qubit, symbols):\n",
        "    \"\"\"Make a Cirq circuit to do a parameterized 'pooling' operation, which\n",
        "    attempts to reduce entanglement down from two qubits to just one.\"\"\"\n",
        "    pool_circuit = cirq.Circuit()\n",
        "    sink_basis_selector = one_qubit_unitary(sink_qubit, symbols[0:3])\n",
        "    source_basis_selector = one_qubit_unitary(source_qubit, symbols[3:6])\n",
        "    pool_circuit.append(sink_basis_selector)\n",
        "    pool_circuit.append(source_basis_selector)\n",
        "    pool_circuit.append(cirq.CNOT(control=source_qubit, target=sink_qubit))\n",
        "    pool_circuit.append(sink_basis_selector**-1)\n",
        "    return pool_circuit"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LoG0a3U_2qGA"
      },
      "source": [
        "作成したものを確認するために、1 キュービットのユニタリー回路を出力しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "T5uhvF-g2rpZ"
      },
      "outputs": [],
      "source": [
        "SVGCircuit(one_qubit_unitary(cirq.GridQubit(0, 0), sympy.symbols('x0:3')))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NWuMb_Us8ar2"
      },
      "source": [
        "次に、2 キュービットのユニタリー回路を出力します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "aJTdRrfS2uIo"
      },
      "outputs": [],
      "source": [
        "SVGCircuit(two_qubit_unitary(cirq.GridQubit.rect(1, 2), sympy.symbols('x0:15')))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EXQD1R_V8jyk"
      },
      "source": [
        "そして 2 キュービットのプーリング回路を出力します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DOHRbkvH2xGK"
      },
      "outputs": [],
      "source": [
        "SVGCircuit(two_qubit_pool(*cirq.GridQubit.rect(1, 2), sympy.symbols('x0:6')))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AzVauXWD3v8C"
      },
      "source": [
        "##### 1.5.2.1 量子畳み込み\n",
        "\n",
        "<a href=\"https://arxiv.org/abs/1810.03787\" class=\"external\">Cong と Lukin</a> の論文にあるとおり、1 次元量子畳み込みを、ストライド 1 の隣接するすべてのキュービットペアに 2 キュービットのパラメーター化されたユニタリの適用として定義します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "1Fa19Lzb3wnR"
      },
      "outputs": [],
      "source": [
        "def quantum_conv_circuit(bits, symbols):\n",
        "    \"\"\"Quantum Convolution Layer following the above diagram.\n",
        "    Return a Cirq circuit with the cascade of `two_qubit_unitary` applied\n",
        "    to all pairs of qubits in `bits` as in the diagram above.\n",
        "    \"\"\"\n",
        "    circuit = cirq.Circuit()\n",
        "    for first, second in zip(bits[0::2], bits[1::2]):\n",
        "        circuit += two_qubit_unitary([first, second], symbols)\n",
        "    for first, second in zip(bits[1::2], bits[2::2] + [bits[0]]):\n",
        "        circuit += two_qubit_unitary([first, second], symbols)\n",
        "    return circuit"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fTzOm_t394Gj"
      },
      "source": [
        "（非常に水平な）回路を表示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Bi6q2nmY3z_U"
      },
      "outputs": [],
      "source": [
        "SVGCircuit(\n",
        "    quantum_conv_circuit(cirq.GridQubit.rect(1, 8), sympy.symbols('x0:15')))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3svBAfap4xhP"
      },
      "source": [
        "##### 1.5.2.2 量子プーリング\n",
        "\n",
        "量子プーリングレイヤーは、上記で定義された 2 キュービットプールを使用して、$N$ キュービットから $\\frac{N}{2}$ キュービットまでをプーリングします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jD3fgcWO4yEU"
      },
      "outputs": [],
      "source": [
        "def quantum_pool_circuit(source_bits, sink_bits, symbols):\n",
        "    \"\"\"A layer that specifies a quantum pooling operation.\n",
        "    A Quantum pool tries to learn to pool the relevant information from two\n",
        "    qubits onto 1.\n",
        "    \"\"\"\n",
        "    circuit = cirq.Circuit()\n",
        "    for source, sink in zip(source_bits, sink_bits):\n",
        "        circuit += two_qubit_pool(source, sink, symbols)\n",
        "    return circuit"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NX83NHDP_Q_Z"
      },
      "source": [
        "プーリングコンポーネント回路を調べます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pFXow2OX47O5"
      },
      "outputs": [],
      "source": [
        "test_bits = cirq.GridQubit.rect(1, 8)\n",
        "\n",
        "SVGCircuit(\n",
        "    quantum_pool_circuit(test_bits[:4], test_bits[4:], sympy.symbols('x0:6')))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "23VcPLT45Lg7"
      },
      "source": [
        "### 1.6 モデルの定義\n",
        "\n",
        "定義したレイヤーを使用して純粋な量子 CNN を構築します。8 キュービットで開始し、1 キュービットまでプールダウンしてから、$\\langle \\hat{Z} \\rangle$ を測定します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vzEsY6-n5NR0"
      },
      "outputs": [],
      "source": [
        "def create_model_circuit(qubits):\n",
        "    \"\"\"Create sequence of alternating convolution and pooling operators \n",
        "    which gradually shrink over time.\"\"\"\n",
        "    model_circuit = cirq.Circuit()\n",
        "    symbols = sympy.symbols('qconv0:63')\n",
        "    # Cirq uses sympy.Symbols to map learnable variables. TensorFlow Quantum\n",
        "    # scans incoming circuits and replaces these with TensorFlow variables.\n",
        "    model_circuit += quantum_conv_circuit(qubits, symbols[0:15])\n",
        "    model_circuit += quantum_pool_circuit(qubits[:4], qubits[4:],\n",
        "                                          symbols[15:21])\n",
        "    model_circuit += quantum_conv_circuit(qubits[4:], symbols[21:36])\n",
        "    model_circuit += quantum_pool_circuit(qubits[4:6], qubits[6:],\n",
        "                                          symbols[36:42])\n",
        "    model_circuit += quantum_conv_circuit(qubits[6:], symbols[42:57])\n",
        "    model_circuit += quantum_pool_circuit([qubits[6]], [qubits[7]],\n",
        "                                          symbols[57:63])\n",
        "    return model_circuit\n",
        "\n",
        "\n",
        "# Create our qubits and readout operators in Cirq.\n",
        "cluster_state_bits = cirq.GridQubit.rect(1, 8)\n",
        "readout_operators = cirq.Z(cluster_state_bits[-1])\n",
        "\n",
        "# Build a sequential model enacting the logic in 1.3 of this notebook.\n",
        "# Here you are making the static cluster state prep as a part of the AddCircuit and the\n",
        "# \"quantum datapoints\" are coming in the form of excitation\n",
        "excitation_input = tf.keras.Input(shape=(), dtype=tf.dtypes.string)\n",
        "cluster_state = tfq.layers.AddCircuit()(\n",
        "    excitation_input, prepend=cluster_state_circuit(cluster_state_bits))\n",
        "\n",
        "quantum_model = tfq.layers.PQC(create_model_circuit(cluster_state_bits),\n",
        "                               readout_operators)(cluster_state)\n",
        "\n",
        "qcnn_model = tf.keras.Model(inputs=[excitation_input], outputs=[quantum_model])\n",
        "\n",
        "# Show the keras plot of the model\n",
        "tf.keras.utils.plot_model(qcnn_model,\n",
        "                          show_shapes=True,\n",
        "                          show_layer_names=False,\n",
        "                          dpi=70)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9jqTEe5VSbug"
      },
      "source": [
        "### 1.7 モデルをトレーニングする\n",
        "\n",
        "この例を単純化するために、完全なバッチでモデルをトレーニングします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_TFkAm1sQZEN"
      },
      "outputs": [],
      "source": [
        "# Generate some training data.\n",
        "train_excitations, train_labels, test_excitations, test_labels = generate_data(\n",
        "    cluster_state_bits)\n",
        "\n",
        "\n",
        "# Custom accuracy metric.\n",
        "@tf.function\n",
        "def custom_accuracy(y_true, y_pred):\n",
        "    y_true = tf.squeeze(y_true)\n",
        "    y_pred = tf.map_fn(lambda x: 1.0 if x >= 0 else -1.0, y_pred)\n",
        "    return tf.keras.backend.mean(tf.keras.backend.equal(y_true, y_pred))\n",
        "\n",
        "\n",
        "qcnn_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.02),\n",
        "                   loss=tf.losses.mse,\n",
        "                   metrics=[custom_accuracy])\n",
        "\n",
        "history = qcnn_model.fit(x=train_excitations,\n",
        "                         y=train_labels,\n",
        "                         batch_size=16,\n",
        "                         epochs=25,\n",
        "                         verbose=1,\n",
        "                         validation_data=(test_excitations, test_labels))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2tiCJOb5Qzcr"
      },
      "outputs": [],
      "source": [
        "plt.plot(history.history['loss'][1:], label='Training')\n",
        "plt.plot(history.history['val_loss'][1:], label='Validation')\n",
        "plt.title('Training a Quantum CNN to Detect Excited Cluster States')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Loss')\n",
        "plt.legend()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GyrkcEReQ5Bc"
      },
      "source": [
        "## 2. ハイブリッドモデル\n",
        "\n",
        "量子畳み込みを使用して 8 キュービットから 1 キュービットにする必要はありません。量子畳み込みの 1～2 ラウンドを実行し、結果を従来のニューラルネットワークにフィードすることも可能です。このセクションでは、量子と従来のハイブリッドモデルを説明します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "A2tOK22t7Kjm"
      },
      "source": [
        "### 2.1 単一量子フィルタを備えたハイブリッドモデル\n",
        "\n",
        "量子畳み込みのレイヤーを 1 つ適用し、すべてのビットの $\\langle \\hat{Z}_n \\rangle$ を読み取り、続いて密に接続されたニューラルネットワークを読み取ります。\n",
        "\n",
        "<img src=\"./images/qcnn_5.png\" width=\"1000\">"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lKXuOApgWYFa"
      },
      "source": [
        "#### 2.1.1 モデルの定義"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Ut-U1hBkQ8Fs"
      },
      "outputs": [],
      "source": [
        "# 1-local operators to read out\n",
        "readouts = [cirq.Z(bit) for bit in cluster_state_bits[4:]]\n",
        "\n",
        "\n",
        "def multi_readout_model_circuit(qubits):\n",
        "    \"\"\"Make a model circuit with less quantum pool and conv operations.\"\"\"\n",
        "    model_circuit = cirq.Circuit()\n",
        "    symbols = sympy.symbols('qconv0:21')\n",
        "    model_circuit += quantum_conv_circuit(qubits, symbols[0:15])\n",
        "    model_circuit += quantum_pool_circuit(qubits[:4], qubits[4:],\n",
        "                                          symbols[15:21])\n",
        "    return model_circuit\n",
        "\n",
        "\n",
        "# Build a model enacting the logic in 2.1 of this notebook.\n",
        "excitation_input_dual = tf.keras.Input(shape=(), dtype=tf.dtypes.string)\n",
        "\n",
        "cluster_state_dual = tfq.layers.AddCircuit()(\n",
        "    excitation_input_dual, prepend=cluster_state_circuit(cluster_state_bits))\n",
        "\n",
        "quantum_model_dual = tfq.layers.PQC(\n",
        "    multi_readout_model_circuit(cluster_state_bits),\n",
        "    readouts)(cluster_state_dual)\n",
        "\n",
        "d1_dual = tf.keras.layers.Dense(8)(quantum_model_dual)\n",
        "\n",
        "d2_dual = tf.keras.layers.Dense(1)(d1_dual)\n",
        "\n",
        "hybrid_model = tf.keras.Model(inputs=[excitation_input_dual], outputs=[d2_dual])\n",
        "\n",
        "# Display the model architecture\n",
        "tf.keras.utils.plot_model(hybrid_model,\n",
        "                          show_shapes=True,\n",
        "                          show_layer_names=False,\n",
        "                          dpi=70)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qDqoLZJuWcgH"
      },
      "source": [
        "#### 2.1.2 モデルをトレーニングする"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EyYw9kYIRCE7"
      },
      "outputs": [],
      "source": [
        "hybrid_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.02),\n",
        "                     loss=tf.losses.mse,\n",
        "                     metrics=[custom_accuracy])\n",
        "\n",
        "hybrid_history = hybrid_model.fit(x=train_excitations,\n",
        "                                  y=train_labels,\n",
        "                                  batch_size=16,\n",
        "                                  epochs=25,\n",
        "                                  verbose=1,\n",
        "                                  validation_data=(test_excitations,\n",
        "                                                   test_labels))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "yL3jhGiBRJHt"
      },
      "outputs": [],
      "source": [
        "plt.plot(history.history['val_custom_accuracy'], label='QCNN')\n",
        "plt.plot(hybrid_history.history['val_custom_accuracy'], label='Hybrid CNN')\n",
        "plt.title('Quantum vs Hybrid CNN performance')\n",
        "plt.xlabel('Epochs')\n",
        "plt.legend()\n",
        "plt.ylabel('Validation Accuracy')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NCNiNvheRNzq"
      },
      "source": [
        "ご覧のとおり、非常に控えめな古典的支援により、ハイブリッドモデルは通常、純粋な量子バージョンよりも速く収束します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nVUtWLZnRRDE"
      },
      "source": [
        "### 2.2 多重量子フィルタを備えたハイブリッド畳み込み\n",
        "\n",
        "多重量子畳み込みと従来のニューラルネットワークを使用してそれらを組み合わせるアーキテクチャを試してみましょう。\n",
        "\n",
        "<img src=\"./images/qcnn_6.png\" width=\"1000\">"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ldo_m5P3YBV7"
      },
      "source": [
        "#### 2.2.1 モデルの定義"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "W3TkNVm9RTBj"
      },
      "outputs": [],
      "source": [
        "excitation_input_multi = tf.keras.Input(shape=(), dtype=tf.dtypes.string)\n",
        "\n",
        "cluster_state_multi = tfq.layers.AddCircuit()(\n",
        "    excitation_input_multi, prepend=cluster_state_circuit(cluster_state_bits))\n",
        "\n",
        "# apply 3 different filters and measure expectation values\n",
        "\n",
        "quantum_model_multi1 = tfq.layers.PQC(\n",
        "    multi_readout_model_circuit(cluster_state_bits),\n",
        "    readouts)(cluster_state_multi)\n",
        "\n",
        "quantum_model_multi2 = tfq.layers.PQC(\n",
        "    multi_readout_model_circuit(cluster_state_bits),\n",
        "    readouts)(cluster_state_multi)\n",
        "\n",
        "quantum_model_multi3 = tfq.layers.PQC(\n",
        "    multi_readout_model_circuit(cluster_state_bits),\n",
        "    readouts)(cluster_state_multi)\n",
        "\n",
        "# concatenate outputs and feed into a small classical NN\n",
        "concat_out = tf.keras.layers.concatenate(\n",
        "    [quantum_model_multi1, quantum_model_multi2, quantum_model_multi3])\n",
        "\n",
        "dense_1 = tf.keras.layers.Dense(8)(concat_out)\n",
        "\n",
        "dense_2 = tf.keras.layers.Dense(1)(dense_1)\n",
        "\n",
        "multi_qconv_model = tf.keras.Model(inputs=[excitation_input_multi],\n",
        "                                   outputs=[dense_2])\n",
        "\n",
        "# Display the model architecture\n",
        "tf.keras.utils.plot_model(multi_qconv_model,\n",
        "                          show_shapes=True,\n",
        "                          show_layer_names=True,\n",
        "                          dpi=70)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2eNhDWwKY9N4"
      },
      "source": [
        "#### 2.2.2 モデルをトレーニングする"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "suRvxcAKRZK6"
      },
      "outputs": [],
      "source": [
        "multi_qconv_model.compile(\n",
        "    optimizer=tf.keras.optimizers.Adam(learning_rate=0.02),\n",
        "    loss=tf.losses.mse,\n",
        "    metrics=[custom_accuracy])\n",
        "\n",
        "multi_qconv_history = multi_qconv_model.fit(x=train_excitations,\n",
        "                                            y=train_labels,\n",
        "                                            batch_size=16,\n",
        "                                            epochs=25,\n",
        "                                            verbose=1,\n",
        "                                            validation_data=(test_excitations,\n",
        "                                                             test_labels))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-6NR7yAQRmOU"
      },
      "outputs": [],
      "source": [
        "plt.plot(history.history['val_custom_accuracy'][:25], label='QCNN')\n",
        "plt.plot(hybrid_history.history['val_custom_accuracy'][:25], label='Hybrid CNN')\n",
        "plt.plot(multi_qconv_history.history['val_custom_accuracy'][:25],\n",
        "         label='Hybrid CNN \\n Multiple Quantum Filters')\n",
        "plt.title('Quantum vs Hybrid CNN performance')\n",
        "plt.xlabel('Epochs')\n",
        "plt.legend()\n",
        "plt.ylabel('Validation Accuracy')\n",
        "plt.show()"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "qcnn.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
